package config;

import com.support.LocalDateToLongConverter;
import com.support.LocalDateToStringConverter;
import com.support.LongToLocalDateConverter;
import com.support.StringToLocalDateConverter;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * RestTemplate相关类在spring-web依赖里
 */
@Configuration
@ComponentScan("com.elasticsearchtemplate")
public class ElasticsearchRestTemplateConfig {
    @Bean
    public RestHighLevelClient restHighLevelClient() {
       /* RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
                new HttpHost("localhost", 9200, "http")));
        return client;*/
        return RestClients.create(ClientConfiguration.create("localhost:9200")).rest();
    }

    /**
     * 1.ElasticsearchTemplate已经废弃，建议用ElasticsearchRestTemplate
     * 2.创建ElasticsearchRestTemplate需要RestHighLevelClient对象
     * 3.往es里插入或查询一些数据时，可能会报类型转换相关的异常，比如date相关的类型，
     * 假定你的mapping里面有一个类型为date,格式是yyyy-MM-dd的数据，直接插入LocalDate类型数据就会报错，
     * 解决办法就是在实体类的相关字段上加注解@Field来处理，或者在创建ElasticsearchRestTemplate bean
     * 时进行全局的设置
     * <a href="https://www.cnblogs.com/yourbatman/p/14199830.html#2%E7%AE%A1%E7%90%86%E8%BD%AC%E6%8D%A2%E5%99%A8converterregistry">类型转换服务</a>
     * <a href="https://www.cnblogs.com/binarylei/p/10263589.html">spring conversion service</a>
     * <a href="https://juejin.cn/post/7104138567479197703">spring-data-elasticsearch的转换</a>
     *
     * @return
     */
    //@Bean
    public ElasticsearchRestTemplate abc() {
        //不需要处理自定义转换器时的写法
        ElasticsearchRestTemplate template = new ElasticsearchRestTemplate(restHighLevelClient());
        return template;
    }

    @Bean
    public ElasticsearchRestTemplate def() {
        // 下面是处理了自定义转换器的写法
        MappingElasticsearchConverter elasticsearchConverter = elasticsearchConverter();
        CustomConversions customConversions = customConversions();
        elasticsearchConverter.setConversions(customConversions);
        // 1.afterPropertiesSet方法一定要调用，并且必须在setConversions方法调用之后
        // 因为这样才会把定制的转换器添加到转换服务里了，否则运行的时候报找不到转换器的异常
        // 2.详细原因见SpringDataEsRepositoryConfig2的注释
        elasticsearchConverter.afterPropertiesSet();
        ElasticsearchRestTemplate template = new ElasticsearchRestTemplate(restHighLevelClient(), elasticsearchConverter);
        return template;
    }

    private MappingElasticsearchConverter elasticsearchConverter() {
        MappingElasticsearchConverter mappingElasticsearchConverter = new MappingElasticsearchConverter(
                new SimpleElasticsearchMappingContext());
        // 下面的代码如果在调用setConversions之前调用会导致转换器没有注册到转换服务里(conversionService)
        // mappingElasticsearchConverter.afterPropertiesSet();
        return mappingElasticsearchConverter;
    }

    private CustomConversions customConversions() {
        // es的mapping中指定的字段类型是date，实体类的字段类型是LocalDate，
        // 插入es时会用到LocalDate ->Xxx的转换器,这里采用的是LocalDateToStringConverter。
        // 从es中读取数据到实体类时会用到Xxx->LocalDate转换器,这里采用的是StringToLocalDateConverter。

        // 自定义的转换器建议用enum实现，可以参考ElasticsearchCustomConversions类里面的几个转换器实现

        // es mapping中对birthday指定了date与format为"yyyy-MM-dd"时不能用LocalData<--->Long的转换器，
        // mapping中只指定了类型为date，没有指定format时 可以用LocalData<---> Long的转换器
        //List<Converter> converters = Arrays.asList(LongToLocalDateConverter.INSTANCE, LocalDateToLongConverter.INSTANCE);

        // 把LocalDate数据转换为string的形式保存到es时，符合了es中mapping指定的format显示格式，es保存时仍然是utc的时间数据，见https://www.cnblogs.com/reycg-blog/p/9983251.html
        List<Converter> converters = Arrays.asList(StringToLocalDateConverter.INSTANCE, LocalDateToStringConverter.INSTANCE);

        // String---> LocalDate转换器可以不用加，使用下面的空的converters，调用testFindAllByName测试方法会发现不报错
        //List<Converter> converters = new ArrayList<>();
        ElasticsearchCustomConversions customConversions = new ElasticsearchCustomConversions(converters);

        return customConversions;
    }


}
